package Pages;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;

import javax.imageio.ImageIO;

import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;

import clientMain.*;

/**
 * Browse Items Page
 * @author Andrew Fox
 *
 */

public class BrowsePage extends Page{
	TreeMap<Double,StringBuffer> nextPageProbabilities=new TreeMap<Double,StringBuffer>();
	TreeMap<Integer,Double> listedItemsBids=new TreeMap<Integer,Double>();	// item num, bid
	TreeMap<Integer,Date> listedItemsEndDates=new TreeMap<Integer,Date>();	// item num, date
	double numTabsMean=5.0;
	ArrayList<Integer>openTabs=new ArrayList<Integer>();
	TreeMap<Double,Integer>unopenedItemsProb=new TreeMap<Double,Integer>();
	TreeMap<Integer,Double>itemsProb=new TreeMap<Integer,Double>();

	public BrowsePage(Page page){
//		super(page.url,page.lastURL,page.client);
//		this.html=page.html;
//		this.pageType=page.pageType;
		super(page.url,page.html,page.client,page.pageType);
		this.pageOpenTime=page.pageOpenTime;
		this.lastURL=page.lastURL;
		if(HTML4==true)
			updateItemRatings();
		updateProbabilities();
	}

	/**
	 * Determines the next link to go to in the application
	 * @return the URL or HTML of the next link, depending on whether
	 * 		   the previous entry contained a form or not
	 * @throws IOException 
	 * @throws JsonMappingException 
	 * @throws JsonParseException 
	 */
	public StringBuffer makeDecision() throws JsonParseException, JsonMappingException, IOException{
		StringBuffer nextURL=new StringBuffer(runSettings.rootURL);
		StringBuffer nextLink=new StringBuffer(getRandomStringFromDist(nextPageProbabilities));


		if(verbose)System.out.println("Next Link: "+nextLink);
		// Think Time
		try{Thread.sleep(getThinkTime());}
		catch(InterruptedException e){
			e.printStackTrace();
		}

		if(HTML4==true){
			int start=0;
			int end=0;
			if (nextLink.subSequence(0,7).equals("itemlink")||nextLink.subSequence(0,6).equals("catlink")){
				int numChars=("<label for=\""+nextLink+"\"><a href=\".").length();
				start=html.indexOf("<label for=\""+nextLink+"\"><a href=\".")+numChars;
				end=html.indexOf("\">",start);
				nextURL.append(html.subSequence(start,end));
			}
			else if(nextLink.equals("tabs")){
				int numTabs=0;
				while(numTabs<1&&numTabs<listedItemsBids.size()-1)
					numTabs=(int)expDist(numTabsMean);
				nextURL=openTabs(numTabs);
			}
			else{
				end=html.indexOf("\">"+nextLink);
				start=html.lastIndexOf("<a href=\".",end)+("<a href=\".").length();
				nextURL.append(html.subSequence(start,end));
			}
		}
		else{
			ObjectMapper mapper = new ObjectMapper();
			if(verbose)System.out.println(html);
			JsonNode node = mapper.readValue(html.toString(), JsonNode.class);
			for (int i=0;i<node.size();i++){
				int itemID=Integer.parseInt(node.get(i).get("id").getValueAsText());
				double bid=(node.get(i).get("currentBid").getDoubleValue());
				Date endDate=StringToDate(node.get(i).get("endDate").getValueAsText());
				listedItemsBids.put(itemID, bid);
				listedItemsEndDates.put(itemID, endDate);
				double itemRating=0;
				itemRating+=((double)getNumWords(new StringBuffer(node.get(i).get("name").getValueAsText())))*client.titleWordsFactor;
				itemRating+=((double)getNumWords(new StringBuffer(node.get(i).get("description").getValueAsText())))*client.descriptionWordsFactor;
				client.itemsOfInterest.put(itemID, itemRating);
				if (nextLink.equals("Sell")){
					nextURL=new StringBuffer(nextLink).append("/sellitem");
				}
				else{
					nextURL=new StringBuffer(nextLink).append("/viewitem?id=").append(nextLink);
				}


			}

		}

		return nextURL;
	}

	/**
	 * Updates the item ratings for all items on the page
	 */
	public void updateItemRatings(){
		if(HTML4==true){
			int start=0;
			int end=0;
			int numPics=0;
			start=html.indexOf("<div id=\"entry\">",start);
			while(start!=-1){
				start=html.indexOf("/img/",start)+"/img/".length();
				end=html.indexOf("\" alt=",start);
				if(html.substring(start,end).equals(null))
					numPics=0;
				else
					numPics=1;
				start=html.indexOf("<label for=\"itemlink",end)+("<label for=\"itemlink").length();
				end=html.indexOf("\">",start);
				int itemNum=Integer.parseInt(html.substring(start,end));
				start=html.indexOf("itemID=105\">",end)+"itemID=105\">".length();
				end=html.indexOf("</a>",start);
				StringBuffer itemName=new StringBuffer(html.subSequence(start,end));
				start=html.indexOf("<label for=\"itemBid"+itemNum+"\">",end)+("<label for=\"itemBid"+itemNum+"\">").length();
				end=html.indexOf("</label>",start);
				double itemBid=Double.parseDouble(html.substring(start,end));
				listedItemsBids.put(itemNum, itemBid);
				start=html.indexOf("<label for=\"itemEndDate"+itemNum+"\">",end)+("<label for=\"itemEndDate"+itemNum+"\">").length();
				end=html.indexOf("</label>",start);
				Date biddingEnds=StringToDate(html.substring(start,end));
				listedItemsEndDates.put(itemNum, biddingEnds);
				// Calculate item Rating
				double itemRating=0;
				itemRating+=((double)getNumWords(itemName))*client.titleWordsFactor;
				itemRating+=((double)numPics)*client.numPicsFactor;
				client.itemsOfInterest.put(itemNum,itemRating);

				start=html.indexOf("<div id=\"entry\">",end);
			}
		}
	}

	/**
	 * Updates the probabilities of which page to transition to
	 */
	public void updateProbabilities(){
		double probSum=1;

		double homeProb=0;
		double browseProb=0;
		double sellProb=0.05*Math.random();
		double myAccountProb=0.05*Math.random();
		double logOutProb=0.1*Math.random();
		double previousPageProb=0.05*Math.random();
		double nextPageProb=0.35*Math.random();
		double tabbedBrowsingProb=0.3*Math.random();
		double totalCategoryProb=0.05*Math.random();


		if(HTML4==false){
			homeProb=0;
			browseProb=0;
			sellProb=0.1;
			myAccountProb=0;
			logOutProb=0;
			previousPageProb=0;
			nextPageProb=0;
			totalCategoryProb=0;
		}

		TreeMap<StringBuffer,Double>categoriesProb=new TreeMap<StringBuffer,Double>();
		TreeMap<StringBuffer,Double>itemLinksProb=new TreeMap<StringBuffer,Double>();

		if(HTML4==true){
			if (html.indexOf("Previous Page</a>")==-1)
				previousPageProb=0;
			if (html.indexOf("Next Page ></a>")==-1)
				nextPageProb=0;
		}
		double totalItemLinkProb=1-homeProb-browseProb-sellProb-myAccountProb-logOutProb-previousPageProb-nextPageProb-tabbedBrowsingProb-totalCategoryProb;
		int numItems=listedItemsBids.size();

		for (Integer m:listedItemsBids.keySet())
			if(HTML4){
				itemLinksProb.put(new StringBuffer("itemlink").append(m),totalItemLinkProb/((double)numItems));
				itemsProb.put(m,(double)numItems);
			}
			else{
				itemLinksProb.put(new StringBuffer(Integer.toString(m)),totalItemLinkProb/((double)numItems));
				itemsProb.put(m,(double)numItems);
			}

		unopenedItemsProb=normalizeItemProbs();
		// add categories to probability
		if (HTML4==true){
			int numCategories=0;
			int start=html.lastIndexOf("<label for=\"catlink")+("<label for=\"catlink").length();
			int end=html.indexOf("\">",start);
			if (start!=("<label for=\"catlink").length()-1)
				numCategories=Integer.parseInt(html.substring(start,end));
			for (int i=0;i<numCategories;i++)
				categoriesProb.put(new StringBuffer("catlink").append(i),totalCategoryProb/((double)numCategories));
		}
		nextPageProbabilities.put(probSum,new StringBuffer("Home"));
		probSum-=homeProb;
		nextPageProbabilities.put(probSum,new StringBuffer("Browse"));
		probSum-=browseProb;
		nextPageProbabilities.put(probSum,new StringBuffer("Sell"));
		probSum-=sellProb;
		nextPageProbabilities.put(probSum,new StringBuffer("My Account"));
		probSum-=myAccountProb;
		nextPageProbabilities.put(probSum,new StringBuffer("Log out"));
		probSum-=logOutProb;
		nextPageProbabilities.put(probSum,new StringBuffer("Previous Page"));
		probSum-=previousPageProb;
		nextPageProbabilities.put(probSum,new StringBuffer("Next Page"));
		probSum-=nextPageProb;
		nextPageProbabilities.put(probSum,new StringBuffer("tabs"));
		probSum-=tabbedBrowsingProb;
		for (StringBuffer m:itemLinksProb.keySet()){
			nextPageProbabilities.put(probSum,m);
			probSum-=itemLinksProb.get(m);
		}

	}

	//TODO
	// currently leaves at less likely tab, most likely is first opened tab
	public StringBuffer openTabs(int numTabs){
		String nextTab=null;
		for (int i=0;i<numTabs+1;i++){
			int itemNum=getRandomIntFromDist(unopenedItemsProb);
			if(HTML4){
				int numChars=("<label for=\"itemlink"+itemNum+"\"><a href=\".").length();
				int start=html.indexOf("<label for=\"itemlink"+itemNum+"\"><a href=\".")+numChars;
				int end=html.indexOf("\">",start);
				nextTab=html.substring(start,end);
				if (i!=numTabs){
					// Think Time
					try{Thread.sleep((long)expDist(500));}
					catch(InterruptedException e){
						e.printStackTrace();
					}
					new openNewTab(new StringBuffer(runSettings.rootURL).append(nextTab)).run();
				}
			}
			if (i!=numTabs){
				openTabs.add(itemNum);
				itemsProb.remove(itemNum);
				normalizeItemProbs();	
			}
		}
		return new StringBuffer(runSettings.rootURL).append(nextTab);

	}
	
	public class openNewTab extends Thread{
		StringBuffer url;
		public openNewTab(StringBuffer url){
			this.url=url;
		}
		public void run(){
			StringBuffer newTab=openURL(url,client);
		}

	}

	public TreeMap<Double,Integer>normalizeItemProbs(){
		TreeMap<Double,Integer> map2 = new TreeMap<Double,Integer>();
		double probSum=0;
		for (int m:itemsProb.keySet())
			probSum+=itemsProb.get(m);
		double newProbSum=probSum;
		for (int m:itemsProb.keySet()){
			map2.put(newProbSum/probSum,m);
			newProbSum-=itemsProb.get(m);
		}
		return map2;
	}

	/**
	 * Determines the think time before going to the next page
	 * Deducts from the think time the amount of time since the 
	 * page opened until this calculation is performed
	 * @return Think time in ms
	 */
	public int getThinkTime(){
		int thinkTime=(int)expDist(initialThinkTime);
		thinkTime+=(int)expDist(15000);
		if (verbose)System.out.println("User: "+client.userInfo.username+" - Think Time: "+thinkTime+" ms");
		return (int) ((thinkTime-(new Date().getTime()-pageOpenTime))/runSettings.thinkTimeSpeedUpFactor);
	}

}
